package extractor;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

public class Extractor {
	private File storageDir;
	private String extract = "C:\\Program Files\\WinRAR\\WinRAR.exe";
	private File workingDir;
	private File primary;
	private List<File> extractedFiles = new ArrayList<File>();
	private List<String> mainFiles = new ArrayList<String>();
	private List<File> priorFiles = new ArrayList<File>();
	private final static Logger LOGGER = Logger.getLogger(Extractor.class.getName());

	Extractor(String fileName) {
		super();
		this.storageDir = new File("E:\\Downloads\\");
		this.workingDir = new File(new File(fileName).getAbsoluteFile().getParent());
		LOGGER.fine("Storage directory: " + storageDir.getAbsolutePath());
		LOGGER.fine("Working directory: " + workingDir.getAbsolutePath());
		this.primary = new File(fileName);
	}

	public void beginExtraction() {
		File[] files = this.workingDir.listFiles();
		for(File file : files) {
			this.priorFiles.add(file);
		}
		extract(this.primary);
	}

	public void findAndExtractNestedRar() {
		for(int i=0;i<this.extractedFiles.size();i++) {
			File file = this.extractedFiles.get(i);
			if(file.getName().toLowerCase().endsWith(".r00")) {
				extract(file);
				return;
			}
		}
	}

	public void extract(File file) {
		this.mainFiles.add(getFileNoExtension(file));
		execCommand(extract + " e -ibck " + file + " " + this.workingDir.getAbsolutePath());
		File[] files = this.workingDir.listFiles();
		for(File extractedFile : files) {
			if(!this.priorFiles.contains(extractedFile) && !this.extractedFiles.contains(extractedFile)) {
				String fileName = extractedFile.getName();
				LOGGER.finest("Parsed out file: " + fileName);
				if(!extractedFile.isDirectory()) {
					this.extractedFiles.add(extractedFile);
				}
			}
		}
	}

	private String getFileNoExtension(File file) {
		String fileName = file.getName().toLowerCase(); 
		String[] findExtension = fileName.split("\\.");
		String extension = findExtension[findExtension.length-1];
		return fileName.substring(0, fileName.length()-extension.length());
	}

	public boolean findTargetFiles() {
		boolean result = false;
		for(int i=0;i<this.extractedFiles.size();i++) {
			File file = this.extractedFiles.get(i);
			if(isVideoFile(file) && isMainFile(file)) {
				moveFile(file);
				this.extractedFiles.remove(file);
				result = true;
				break;
			}
			if(isImageFile(file) && isMainFile(file)) {
				moveFile(file);
				this.extractedFiles.remove(file);
				findAndMoveNFO();
				result = true;
				break;
			}
		}
		return result;
	}

	private void findAndMoveNFO() {
		for(int i=0;i<this.extractedFiles.size();i++) {
			File file = this.extractedFiles.get(i);
			if(file.getName().toLowerCase().endsWith(".nfo") && isMainFile(file)) {
				moveFile(file);
				this.extractedFiles.remove(file);
				return;
			}
		}
	}

	private boolean isMainFile(File file) {
		for(int i=0;i<this.mainFiles.size();i++) {
			if(getFileNoExtension(file).equalsIgnoreCase(this.mainFiles.get(i))) {
				return true;
			}
		}
		return false;
	}

	public boolean isVideoFile(File rfile) {
		String fileName = rfile.getName().toLowerCase();
		return (fileName.endsWith(".mkv") || fileName.endsWith(".avi") || fileName.endsWith(".mp4"));
	}

	public boolean isRarFile(File file) {
		String fileName = file.getName().toLowerCase();
		boolean result = fileName.endsWith(".rar");
		result = result || fileName.endsWith(".tar");
		result = result || fileName.endsWith(".r00");
		result = result || fileName.endsWith(".zip");
		result = result || fileName.endsWith(".sfv");
		return result;
	}

	public boolean isImageFile(File file) {
		String fileName = file.getName().toLowerCase();
		if(fileName.endsWith(".iso") || fileName.endsWith(".nrg") || fileName.endsWith(".img")) {
			return true;
		}
		return false;
	}

	public boolean isGarbageFile(File file) {
		String fileName = file.getName().toLowerCase();
		if(fileName.endsWith(".nfo") || file.isDirectory()) {
			return true;
		}
		if(isVideoFile(file) && fileName.contains("sample")) {
			return true;
		}
		if(fileName.matches(".*\\.r\\d\\d")) {
			return true;
		}
		return isRarFile(file);
	}

	public void moveFile(File file) {
		boolean success = file.renameTo(new File(this.storageDir, file.getName()));
		if(success) {
			LOGGER.info("Moved " + file.getName() + " to " + this.storageDir.getAbsolutePath() + " .. AbsPath: " + file.getAbsolutePath());
		}
		else {
			LOGGER.warning("Failed to move " + file.getName() + " to " + this.storageDir.getAbsolutePath());
		}
	}

	public String execCommand(String command) {
		String output = "";
		try {
			LOGGER.finest("input: " + command);
			final long startTime = System.nanoTime();
			Process child = Runtime.getRuntime().exec(command);
			try {                           

				BufferedReader in = new BufferedReader(new InputStreamReader(child.getInputStream()));
				String line = null;
				while ((line = in.readLine()) != null) {
					output = output + line + "\n";
				}
				in.close();

				BufferedReader error = new BufferedReader(new InputStreamReader(child.getErrorStream()));
				String errLine = null;
				while ((errLine = error.readLine()) != null) {
					output = output + errLine + "\n";
				}
				error.close();

				child.waitFor();
				LOGGER.finest("Command: " + command + " took " + (System.nanoTime()-startTime)/1000000000.0 + " seconds to execute.");
			} catch (InterruptedException e) {
				child.destroy();
			}
		} catch (IOException e) {
			LOGGER.warning(e.toString());
		}
		if(output.endsWith("\n")) {
			output = output.substring(0, output.length()-1);
		}
		LOGGER.finest("output: " + output);
		return output;
	}

	public void cleanUp() {
		for(int i=0;i<this.extractedFiles.size();i++) {
			File file = this.extractedFiles.get(i);
			if(isGarbageFile(file)) {
				LOGGER.fine("Flagged " + file.getName() + " as garbage file.");
				if(!file.delete()) {
					LOGGER.info("Failed to delete garbage file: " + file.getName());
				}
			}
			else {
				LOGGER.fine("Did not flag " + file.getName() + " as garbage file.");
			}
		}
		if(!this.primary.delete()) {
			LOGGER.info("Failed to delete primary file: " + this.primary.getName());
		}
	}
}